package com.finance.cooperate.strategy.core.strategy;

import com.alibaba.fastjson.JSON;
import com.finance.cooperate.common.utils.DateUtils;
import com.finance.cooperate.common.utils.ProbabilityUtils;
import com.finance.cooperate.common.utils.StringUtil;
import com.finance.cooperate.dao.entity.secondary.StrategyHitDetails;
import com.finance.cooperate.dao.entity.secondary.StrategyInfo;
import com.finance.cooperate.dao.repository.secondary.StrategyHitDetailsRepository;
import com.finance.cooperate.dao.repository.secondary.StrategyInfoRepository;
import com.finance.cooperate.dao.support.constant.DeleteStatus;
import com.finance.cooperate.dao.support.query.Filter;
import com.finance.cooperate.dao.support.query.QueryParams;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @ClassName AbstractStrategyHandler
 * @Description 策略处理类
 * @Author shenanxin
 * @Date 2023/8/23 15:47
 * @Modify ...
 */
@Slf4j
public abstract class AbstractStrategyHandler implements StrategyHandler{

    // 给个种子
    private final static Long BASE_SEED = 21L;


    @Autowired
    private StrategyScope strategyScope;

    @Autowired
    private StrategyInfoRepository strategyInfoRepository;

    @Autowired
    private StrategyHitDetailsRepository strategyHitDetailsRepository;


    // 用户 id
    protected ThreadLocal<String> uid = ThreadLocal.withInitial(() -> strategyScope.getUid());
    // 申请 id
    protected ThreadLocal<String> applyId = ThreadLocal.withInitial(() -> strategyScope.getApplyId());
    // 策略(原始表数据)
    protected ThreadLocal<List<StrategyInfo>> strategyInfoList = ThreadLocal.withInitial(() -> new ArrayList<>());


    @Override
    public void init() {

        uid.set(strategyScope.getUid());
        applyId.set(strategyScope.getApplyId());


        // 初始化策略
        String name = this.setStrategyName();

        Strategy strategy = new Strategy();
        strategy.setName(name);
        strategy.setInitSore(strategyScope.getInitialScore());

        strategyScope.putResult(this.getClass(), strategy);


    }


    @Override
    public boolean isExecute() {

        Strategy strategy = strategyScope.getResult(this.getClass());

        Long loanNum = strategyScope.getLoanNum();

        if (strategy != null) {

            QueryParams<StrategyInfo> queryParams = new QueryParams<>();

            // 这里有个特殊处理，如果策略 name 是 re_* 格式的归为一个，其它的按照 name 严格匹配
            if (strategy.getName().equals("re_")) {
                queryParams.and(
                        Filter.eq("status", 1),
                        Filter.likeR("name", strategy.getName()),
                        Filter.le("lowerHitScore", strategyScope.getFinalScore()),
                        Filter.ge("upperHitScore", strategyScope.getFinalScore()),
                        Filter.le("lowerLoanNum", loanNum),
                        Filter.ge("upperLoanNum", loanNum),
                        Filter.eq("isDeleted", DeleteStatus.NO)
                );
            } else {
                queryParams.and(
                        Filter.eq("status", 1),
                        Filter.eq("name", strategy.getName()),
                        Filter.le("lowerHitScore", strategyScope.getFinalScore()),
                        Filter.ge("upperHitScore", strategyScope.getFinalScore()),
                        Filter.le("lowerLoanNum", loanNum),
                        Filter.ge("upperLoanNum", loanNum),
                        Filter.eq("isDeleted", DeleteStatus.NO)
                );
            }


            try {

                strategyInfoList.set(strategyInfoRepository.findAll(queryParams));

                // 说明：如果同一策略命中多次，这几个值必须一致：isContinueExecute，weight，group，modelId
                if (strategyInfoList.get().size() > 0) {
                    StrategyInfo strategyInfo = strategyInfoList.get().get(0);

                    // 如果指定了模型，只有该模型匹配才执行
                    if (!StringUtil.isEmptyOrNvl(strategyInfo.getModelId())
                            && !strategyInfo.getModelId().equals(strategyScope.getModelId())) {

                        return false;
                    }

                    // 如果分组设置Group
                    if (!StringUtil.isEmptyOrNvl(strategyInfo.getGroup())) {
                        strategy.setGroup(strategyInfo.getGroup());
                    }

                    strategy.setContinueExecute(strategyInfo.getIsContinueExecute());


                    strategyScope.putResult(this.getClass(), strategy);

                    return true;

                }


            } catch (Exception e) {
                log.error(e.getMessage(), e);
            }


        }


        return false;
    }


    @Override
    public boolean save(Long strategyId) {

        try {
            Strategy strategy = strategyScope.getResult(this.getClass());

            if (strategy.isHit()) {

                StrategyHitDetails hitDetails = new StrategyHitDetails();

                hitDetails.setStrategyId(strategyId);
                hitDetails.setUid(strategyScope.getUid());
                hitDetails.setInitialScore(strategy.getInitSore());
                hitDetails.setFinalScore(strategy.getFinalScore());
                hitDetails.setData(strategy.getData());
                hitDetails.setRecordId(strategyScope.getRecodeId());
                hitDetails.setCreated(DateUtils.getMillis());
                strategyHitDetailsRepository.save(hitDetails);


            }

            return true;

        } catch (Exception e) {
            log.error(e.toString(), e);
        }

        return false;
    }

    @Override
    public void destroy() {
        uid.remove();
        applyId.remove();
        strategyInfoList.remove();
    }

    /**
     * @Author shen
     * @Description 策略命中
     * @Date 14:24 2022/10/22
     * @Param [data]
     * @return boolean
     **/
    protected boolean hitResult(Map<String, Object> data, StrategyInfo strategyInfo) {

        Strategy strategy = strategyScope.getResult(this.getClass());

        if (data != null && !data.isEmpty()) {
            strategy.setData(JSON.toJSONString(data));
        }

        // 改模型分

        // 默认操作为 "="
        String operation = "=";
        if (!StringUtil.isEmptyOrNvl(strategyInfo.getOperation())) {
            operation = strategyInfo.getOperation().trim();
        }

        // 需要更新初始分为，当前分
        strategy.setInitSore(strategyScope.getFinalScore());

        Double initSore = strategy.getInitSore();

        Double changeScore = strategyInfo.getChangeScore();

        // 原始存储模型分转换
        double suffixScore = initSore / 100;
        initSore = initSore.intValue() + suffixScore;

        switch (operation) {
            case "-":

                initSore -= changeScore;
                if (initSore < 0) {
                    initSore = 0d + suffixScore;
                }

                break;
            case "+":

                initSore += changeScore;
                if (initSore >= 100) {
                    initSore = 99d + suffixScore;
                }

                break;
            case "=":
                if (changeScore >= 0 && changeScore < 100) {

                    initSore = changeScore + suffixScore;

                }

                break;

            default:
                return false;
        }


        strategy.setFinalScore(initSore);


        strategy.setHit(true);
        strategyScope.putResult(this.getClass(), strategy);

        return true;
    }


    @Override
    public Long executeControl() {

        try {

            List<StrategyInfo> strategyInfos = strategyInfoList.get();

            for (StrategyInfo strategyInfo : strategyInfos) {

                // 根据权重判断是否执行，默认权重为100
                Double weight = strategyInfo.getWeight();

                if (weight == null) {
                    weight = 100.0;
                }


                if (ProbabilityUtils.isHit(weight, BASE_SEED * uid.get().hashCode())) {

                    Long strategyId = this.execute(strategyInfo);

                    if (strategyId != null) {
                        return strategyId;
                    }

                }

            }

        } catch (Exception e) {
            log.error("executeControl execution exception：", e);
        }

        return null;
    }
}
